﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using DrawableObjects;
using DrawableObjects.Actors;

namespace LightEffects
{
    /// <summary>
    /// GlobalLight represents the sun and calculates the shadowmap.
    /// </summary>
    public class GlobalLight
    {

        public Vector3 color
        {
            get;
            set;
        }

        public float intensity
        {
            get;
            set;
        }

        public Vector3 direction
        {
            get;
            set;
        }

        private GlobalLight(Vector3 color, float intensity, Vector3 direction)
        {
            this.color = color;
            this.intensity = intensity;
            this.direction = direction;
        }

        public static GlobalLight CreateGlobalLight()
        {
            return new GlobalLight(Color.White.ToVector3(), 1f, Vector3.Normalize(new Vector3(0.5f, 1, 1)));
        }

        public void createShadowMap(GraphicsDevice device, Actor[] shadowcastingObjects, Camera camera, Effect shader_ShadowMap, RenderTarget2D shadowMap)
        {
            Matrix lightviewProjectionMatrix = CreateLightViewProjectionMatrix(camera);
            device.BlendState = BlendState.Opaque;
            device.DepthStencilState = DepthStencilState.Default;
            device.SetRenderTarget(shadowMap);

            device.Clear(Color.White);

            foreach (Actor actor in shadowcastingObjects)
            {
                DrawModelForShadowMap(actor, lightviewProjectionMatrix, shader_ShadowMap);
            }
            device.SetRenderTarget(null);
        }

        private void DrawModelForShadowMap(Actor actor, Matrix lightViewProjection, Effect shader_ShadowMap)
        {
            foreach (ModelMesh mesh in actor.model.Meshes)
            {
                foreach (ModelMeshPart meshPart in mesh.MeshParts)
                {
                    meshPart.Effect = shader_ShadowMap;
                    meshPart.Effect.CurrentTechnique = meshPart.Effect.Techniques["CreateShadowMap"];
                    meshPart.Effect.Parameters["World"].SetValue(actor.worldMatrix);
                    meshPart.Effect.Parameters["LightViewProj"].SetValue(lightViewProjection);
               
                }
                mesh.Draw();
            }
        }

        public void Update()
        {
            direction = direction - new Vector3(0, 0, 0.0001f); //simulates the movement of the sun
            color -= new Vector3(0, 0.0001f, 0.0001f); //simulates the color of a sunset
        }

        public Matrix CreateLightViewProjectionMatrix(Camera cam)
        {
            
            BoundingFrustum cameraFrustum = new BoundingFrustum(cam.viewMatrix * cam.projectionMatrix);
            Matrix lightRotation = Matrix.CreateLookAt(Vector3.Zero, -direction, Vector3.Up);

            Vector3[] frustumCorners = cameraFrustum.GetCorners();

            for (int i = 0; i < frustumCorners.Length; i++)
            {
                frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightRotation);
            }

            BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners);

            Vector3 boxSize = lightBox.Max - lightBox.Min;
            Vector3 halfBoxSize = boxSize * 0.5f;


            Vector3 lightPosition = lightBox.Min + halfBoxSize;
            lightPosition.Z = lightBox.Min.Z;


            lightPosition = Vector3.Transform(lightPosition, Matrix.Invert(lightRotation));

            Matrix lightView = Matrix.CreateLookAt(lightPosition, lightPosition - direction, Vector3.Up);


            Matrix lightProjection = Matrix.CreateOrthographic(boxSize.X, boxSize.Y, -boxSize.Z, boxSize.Z);

            return lightView * lightProjection;
        }

    }
}

